package com.example.lotterysystem.service.mq;

import cn.hutool.core.date.DateUtil;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.MailUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import com.example.lotterysystem.dao.dataobject.WinningRecordDO;
import com.example.lotterysystem.dao.mapper.ActivityPrizeMapper;
import com.example.lotterysystem.dao.mapper.WinningRecordMapper;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.activitystatus.ActivityStatusManager;
import com.example.lotterysystem.service.dto.ConvertActivityStatusDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import com.example.lotterysystem.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.QUEUE_NAME;

/**
 * Created with IntelliJ IDEA.
 * Description: RabbitMq消息处理
 * User: Spider-Man
 * Date: 2025-09-14
 * Time: 21:56
 *
 * @RabbitListener() 监听器,监听哪个队列,队列获取到消息之后,就可以转发给注解所在的类了
 */
@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqRecevier {
    private static final Logger logger = LoggerFactory.getLogger(MqRecevier.class);
    @Autowired
    private DrawPrizeService drawPrizeService;
    // @RabbitHandler 处理消息的注解

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private ActivityStatusManager activityStatusManager; //活动状态信息扭转处理

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

  /*  @Autowired
    private SMSUtil smsUtil;*/
    @RabbitHandler
    public void process(Map<String,String> message) {
        logger.info("MQ成功接收到消息.message:{}",
                JacksonUtil.writeValueAsString(message));
        //获取到消息中的数据
        String paranString = message.get("messageData");
        //需要进行反序列化
        DrawPrizeParam param = JacksonUtil.readValue(paranString,DrawPrizeParam.class);
        /**
         * 处理抽奖的流程,任何异步只要捕获到异常,我们则进行相关数据的处理
         */
        try{
            //1.校验抽奖请求是否有效
            if(!drawPrizeService.checkDrawPrizeParam(param)){
                return;
            }

            //2.状态扭转(设计模式)
            statusConvert(param);
            //3.保存中奖名单
            List<WinningRecordDO> winningRecordDOList = drawPrizeService.saveWinnerRecords(param);
            //4.通知中奖者(邮箱,短信)
            //通知流程:  异步进行处理
            syncExecute(winningRecordDOList);
        } catch (ServiceException e) {
            logger.error("处理 MQ 消息异常! {}:{}",e.getCode(),e.getMessage(),e);
            //5.出现异常,需要保证事务一致性(回滚). 排除异常
            rollback(param);
            //抛出异常: 进行消息重试
        } catch (Exception e) {
            logger.error("处理MQ消息异常",e);
        }

    }

    /**
     * 处理异常的回滚行为: 恢复处理请求之前库的状态
     * activity activity_prize activity_user 三个表
     * winning
     * @param param
     */
    private void rollback(DrawPrizeParam param) {
        //将已经改变的数据进行流转
        //1. 回滚状态(activity activity_prize activity_user)
        //扭转状态是添加事务的, 所以需要先进行判断是否需要扭转
        if(!statusNeedRollback(param)) {
            return; // 不需要回滚
        }
        //进行回滚
        rollbackStatus(param);

        //2.回滚中奖者名单(winner_record) --- 只有状态流转完成了才有可能这个中奖者名单进行回滚
        if(!winnerNeedRollback(param)) {
            //不需要回滚
            return;
        }
        //进行回滚
        rollbackWinner(param);
    }

    /**
     * 回滚中奖记录 删除奖品下的中奖用户
     * @param param
     */
    private void rollbackWinner(DrawPrizeParam param) {
        drawPrizeService.deleteRecords(param.getActivityId(),param.getPrizeId());
    }


    /**
     * 回滚相关状态数据
     * @param param
     */
    private void rollbackStatus(DrawPrizeParam param) {
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.INIT);
        convertActivityStatusDTO.setUserIds(param.getWinnerList().stream().
                map(DrawPrizeParam.Winner :: getUserId).collect(Collectors.toList()));
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.INIT);
        activityStatusManager.rollbackHandlerEvent(convertActivityStatusDTO);
    }

    /**
     * 判断中奖名单是否需要回滚
     */
    private boolean winnerNeedRollback(DrawPrizeParam param) {
        //判断活动中的奖品是否存在中奖者(就可以判断是否有中奖者)
        int count = winningRecordMapper.countByAPId(param.getActivityId(),param.getPrizeId());
        return count > 0;
    }

    //判断状态是否需要进行回滚
    private boolean statusNeedRollback(DrawPrizeParam param) {
        /**
         * 扭转状态时保证了本地事务一致性的(@Transactionl注解)
         * 只需判断奖品或人员是否扭转过(不包括活动) 就可以判断出全部状态是否扭转
         */
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //已经扭转了,需要回滚
            return true;
        }
        return false;
    }

    /**
     * 并发处理抽奖后续流程
     * @param winningRecordDOList
     */
    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {
        //通过线程池处理异步任务

        /*//短信通知
        threadPoolTaskExecutor.execute(() -> sendMessage(winningRecordDOList));*/

        //邮件通知
        threadPoolTaskExecutor.execute(() -> sendMail(winningRecordDOList));
    }

    //发送邮件
    private void sendMail(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖列表为空,不用发送邮件");
            return;
        }
        for(WinningRecordDO winningRecordDO : winningRecordDOList) {
            //Hi,牛孟俊。恭喜你在抽奖活动活动中获得一等奖:显卡。获奖奖时间为18:18:44,请尽快领取您的奖励
            String context = "Hi,"+winningRecordDO.getWinnerName()+
                    "。恭喜你在抽奖活动活动中获得"+ ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage()
                    +": " +winningRecordDO.getPrizeName()+"。获奖奖时间为"+ DateUtil.formatTime(winningRecordDO.getWinningTime()) +
                    ",请尽快领取您的奖励!";
            mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(),"中奖通知",
                    context);
        }
    }

    /*//短信通知
    private void sendMessage(List<WinningRecordDO> winningRecordDOList) {
         if (CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("中奖列表为空，不用发短信！");
            return;
        }
        for (WinningRecordDO winningRecordDO : winningRecordDOList) {
            Map<String, String> map = new HashMap<>();
            map.put("name", winningRecordDO.getWinnerName());
            map.put("activityName", winningRecordDO.getActivityName());
            map.put("prizeTiers", ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMessage());
            map.put("prizeName", winningRecordDO.getPrizeName());
            map.put("winningTime", DateUtil.formatTime(winningRecordDO.getWinningTime()));
            smsUtil.sendMessage("SMS_465985911",
                    winningRecordDO.getWinnerPhoneNumber().getValue(),
                    JacksonUtil.writeValueAsString(map));
        }
    }*/
    /**
     * 状态扭转 --- 采用设计模式
     * 1. 活动状态
     * 2. 关联奖品状态
     * 3. 关联人员状态
     * @param param
     */

    private void statusConvert(DrawPrizeParam param){
        ConvertActivityStatusDTO convertActivityStatusDTO = new ConvertActivityStatusDTO();
        convertActivityStatusDTO.setActivityId(param.getActivityId());
        convertActivityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        convertActivityStatusDTO.setPrizeId(param.getPrizeId());
        convertActivityStatusDTO.setTargetPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        convertActivityStatusDTO.setUserIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner :: getUserId)
                .collect(Collectors.toList())
        );
        convertActivityStatusDTO.setTargetUserStatus(ActivityUserStatusEnum.COMPLETED);
        activityStatusManager.handlerEvent(convertActivityStatusDTO);
    }

    /**
     * //未采用设计模式的扭转思路
     * private void statusConvert(DrawPrizeParam param) {
     *
     *         //活动: RUNNING ---> COMPLETED (在全部奖品抽取完之后才进行扭转)
     *         //奖品: INIT ---> COMPLETED
     *         //人员列表: INIT ---> COMPLETED
     *
     *         //1. 扭转奖品状态
     *         //查询活动关联的奖品信息, 判断当前奖品状态是否已完成(COMPLETED)
     *
     *         //2. 扭转人员状态
     *         //查询活动关联的人员信息, 判断当前奖品状态是否已完成(COMPLETED)
     *
     *
     *         //3. 扭转活动状态
     *         //查询活动信息, 判断当前活动状态是否已完成(COMPLETED)
     *
     *
     *         //4.更新缓存中的数据
     *     }
     *
     */

}
